package com.yiquan.user.service;

import com.yiquan.user.dao.db.UserDao;
import com.yiquan.user.dao.es.EsUserDao;
import com.yiquan.user.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @author Tyrone
 * @date 2021/12/23 20:17
 */
@Service
@Slf4j
public class LoginService {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private BCryptPasswordEncoder encoder;

    @Autowired
    private UserDao userDao;

    @Autowired
    private EsUserDao esUserDao;
    /**
     * 验证码前缀
     */
    public static final String SMS_PREFIX = "SMS_";
    public static final String SIMPLE_CODE_PREFIX = "SIMPLE_CODE_";
    public static final String TEMPORARY_USER_ID = "TEMPORARY_USER_ID";
    public static final String TEMPORARY_USER_ID_BUCKET = "TEMPORARY_USER_ID_BUCKET";

    private static final String DEFAULT_AVATAR_SUFFIX = "https://bookpavilion.oss-cn-shenzhen.aliyuncs.com/yi_quan/default_avatar/avatar-";

    public User loginBySimpleWay(String username, String password) {

        /**
         * 由于注册时候，密码通过BCryptPasswordEncoder加密过，
         * 所以这里也需要解密
         */
        String encode = encoder.encode(password);
        User user = userDao.findUserByUsername(username);
        if (user != null && encoder.matches(password, user.getPassword())) {
            return user;
        }
        return null;
    }

    public boolean sendSms(String phone) {
        int code = RandomUtils.nextInt(1000, 9999);
        log.info("发送给手机号：{} 的验证码为：{}", phone, code);
        redisTemplate.opsForValue().set(SMS_PREFIX + phone, String.valueOf(code), 5, TimeUnit.MINUTES);
        Map<String, String> dataMap = new HashMap<>();
        dataMap.put("phone", phone);
        dataMap.put("code", String.valueOf(code));
        rabbitTemplate.convertAndSend("yiquan_sms", dataMap);
        return true;
    }

    public String sendSimpleCode(String username) {
        int code = RandomUtils.nextInt(1000, 9999);
        log.info("发送给账号：{} 的验证码为：{}", username, code);
        redisTemplate.opsForValue().set(SMS_PREFIX + username, String.valueOf(code), 5, TimeUnit.MINUTES);
        return String.valueOf(code);
    }

    public User loginBySms(String phone, String code) {
        String correctCode = (String) redisTemplate.opsForValue().get(SMS_PREFIX + phone);
        if (correctCode.equals(code)) {
            return userDao.findUserByPhone(phone);
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean registerBySms(User user, String code) {
        String correctCode = (String) redisTemplate.opsForValue().get(SMS_PREFIX + user.getPhone());
        if (correctCode != null && correctCode.equals(code)) {
            user.setAvatar(DEFAULT_AVATAR_SUFFIX + RandomUtils.nextInt(1, 5) + ".jpg");
            user.setRegion("广东 江门");
            user.setUser_type("N");
            user.setNick_name(randomString(CHARS,6));
            user.setStatus("0");
            user.setProfile("这个人很懒，什么也没留下");
            user.setBackground("https://bookpavilion.oss-cn-shenzhen.aliyuncs.com/yi_quan/default_background.jpg");
            user.setAvatar(DEFAULT_AVATAR_SUFFIX + RandomUtils.nextInt(1, 5) + ".jpg");
            userService.add(user);
            esUserDao.save(user);
            return true;
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    // TODO: 2021/12/27 是否需要图片验证码待确认
    public boolean registerBySimple(User user, String code) {
        String correctCode = (String) redisTemplate.opsForValue().get(SIMPLE_CODE_PREFIX + user.getUsername());
        if ("000000".equals(code)) {
            User existedUser = this.loginBySimpleWay(user.getUsername(), user.getPassword());
            if (existedUser != null) {
                return false;
            }
            user.setNick_name(randomString(CHARS,6));
            user.setRegion("广东 江门");
            user.setProfile("这个人很懒，什么也没留下");
            user.setStatus("0");
            user.setUser_type("N");
            user.setBackground("https://bookpavilion.oss-cn-shenzhen.aliyuncs.com/yi_quan/default_background.jpg");
            user.setAvatar(DEFAULT_AVATAR_SUFFIX + RandomUtils.nextInt(1, 5) + ".jpg");
            userService.add(user);
            esUserDao.save(user);

            return true;
        }
        return false;
    }


    /**
     * 获取临时用户id并放入临时用户id桶内(10000000-19999999)
     *
     * @return
     */
    public Long getTemporaryUserId() {
        redisTemplate.opsForValue().setIfAbsent(TEMPORARY_USER_ID, 10000000L);
        Long temporaryUserId = redisTemplate.opsForValue().increment(TEMPORARY_USER_ID);
        redisTemplate.opsForSet().add(TEMPORARY_USER_ID_BUCKET, temporaryUserId);
        return temporaryUserId;
    }
    private static final String CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";


    /**
     * 获取随机字符串
     * @param sampleString 样本字符串
     * @param length 生成的随机字符串长度
     * @return 返回随机字符串
     */
    public static String randomString(String sampleString, int length) {
        if (sampleString == null || sampleString.length() == 0) {
            return "";
        }
        if (length < 1) {
            length = 1;
        }
        final StringBuilder sb = new StringBuilder(length);
        int baseLength = sampleString.length();
        while (sb.length() < length) {
            //此处用ThreadLocalRandom 不用Random的感兴趣的同学可以看看这俩的区别
            //主要区别在于多线程高并发环境下 ThreadLocalRandom比Random生成随机数的速度快
            int number = ThreadLocalRandom.current().nextInt(baseLength);
            sb.append(sampleString.charAt(number));
        }
        return sb.toString();
    }

}
